package com.imooc.distributedemo.resume.arith;

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class MyBlockingQueue<T> {
    private LinkedList<T> queue;
    private int capacity;
    private ReentrantLock lock;
    private Condition notFull;
    private Condition notEmpty;

    /**
     * LinkedList 作为队列的底层数据结构，使用 Lock 和 Condition 接口来实现阻塞队列
     * lock 用于同步队列的插入和删除操作；
     * notFull 用于在队列已满时阻塞生产者线程；
     * notEmpty 用于在队列为空时阻塞消费者线程。
     * @param queue
     * @param capacity
     * @param lock
     * @param notFull
     * @param notEmpty
     */
    public MyBlockingQueue(LinkedList<T> queue, int capacity, ReentrantLock lock, Condition notFull, Condition notEmpty) {
        this.queue = queue;
        this.capacity = capacity;
        this.lock = lock;
        this.notFull = notFull;
        this.notEmpty = notEmpty;
    }

    public void put(T element) throws InterruptedException{
        lock.lock();
        try{
            while (queue.size()==capacity){
                notFull.await();
            }
            queue.add(element);
            notEmpty.signal();
        }finally {
            lock.unlock();
        }
    }

    public T take() throws InterruptedException{
        lock.lock();
        try{
            while (queue.isEmpty()){
                notEmpty.await();
            }
            T element = queue.remove();
            notFull.signal();
            return element;
        }finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        int capacity = 10;
        ReentrantLock lock = new ReentrantLock();
        Condition notFull = lock.newCondition();
        Condition notEmpty = lock.newCondition();
        MyBlockingQueue<Integer> queue = new MyBlockingQueue<>(
                list,capacity,lock,notFull,notEmpty
        );

        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    queue.put(i);
                    System.out.println("Producer put: " + i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 20; i++) {
                try {
                    Integer element = queue.take();
                    System.out.println("Consumer take: " + element);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}
